package com.mybatis.spring;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.mybatis.anno.Delete;
import com.mybatis.anno.Insert;
import com.mybatis.anno.Result;
import com.mybatis.anno.ResultMap;
import com.mybatis.anno.ResultMapStruct;
import com.mybatis.anno.ResultMapStructLists;
import com.mybatis.anno.Select;
import com.mybatis.anno.Update;

public class MapperProxy implements InvocationHandler {

	private static Map<String,ResultMapStruct> resultMaps=new HashMap<>();
	private Class<?> mapperClass;
	@SuppressWarnings("unchecked")
	public  <T> T getMapper(Class<?> mapperClass) {
		this.mapperClass = mapperClass;
		return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
				new Class<?>[] { mapperClass }, this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
		Object res = null;
		parseResult();
		Insert insert = method.getDeclaredAnnotation(Insert.class);
		Update update = method.getDeclaredAnnotation(Update.class);
		Delete delete = method.getDeclaredAnnotation(Delete.class);
		Select select = method.getDeclaredAnnotation(Select.class);
		String sql = null;
		if(insert!=null) {
			sql=insert.value();
		}else if(update!=null) {
			sql=update.value();
		}else if(delete!=null) {
			sql=delete.value();
		}else if(select!=null) {
			sql=select.value();
		}
		Map<String, String> map = handleSqlForParam(sql);
		Connection connection=getSqlConnection();
		PreparedStatement ps=connection.prepareStatement(handleSql(sql));
		if(args!=null&&args.length==1) {
			Object entity = args[0];
			int index=1;
			for (String key : map.keySet()) {
				Object value = invokeGetter(entity,key);
				ps.setObject(index, value);
				index++;
			}
		}
		
		if(isDDL(method.getAnnotations())) {
			res=ps.executeUpdate();
		}else {
			ResultMap resultMap = method.getDeclaredAnnotation(ResultMap.class);
			if(resultMap!=null) {
				ResultMapStruct results=resultMaps.get(resultMap.value());
				ResultSet rs=ps.executeQuery();
				List<Object> list=null;
				while(rs.next()) {
					if(method.getReturnType().equals(List.class)) {
						Type type = method.getGenericReturnType();
						Type entityType=null;
						if (type instanceof ParameterizedType) {
							Type[] typesto = ((ParameterizedType) type).getActualTypeArguments();
							entityType=typesto[0];
						}
						if(list==null) {
							list=new ArrayList<>();
						}
						Object obj = Class.forName(entityType.getTypeName()).newInstance();
						for (Result result : results.value()) {
							invokeSetter(obj, result.property(),rs.getObject(result.column()));
						}
						list.add(obj);
					}else {
						Class<?> entityType=method.getReturnType();
						Object obj = entityType.newInstance();
						for (Result result : results.value()) {
							invokeSetter(obj, result.property(),rs.getObject(result.column()));
						}
						connection.close();
						return obj;
					}
				}
				connection.close();
				return list;
			}
		}
		connection.close();
		return res;
		
	}

	private void parseResult() {
		ResultMapStructLists results = mapperClass.getDeclaredAnnotation(ResultMapStructLists.class);
		if(results!=null) {
			for (ResultMapStruct resultMapStruct : results.value()) {
				resultMaps.put(resultMapStruct.id(), resultMapStruct);
			}
			
		}
	}

	private boolean isDDL(Annotation[] annotations) {
		for (int i = 0; i < annotations.length; i++) {
			Annotation annotation =	annotations[i];
			if(annotation instanceof Insert) {
				return true;
			}else if(annotation instanceof Update) {
				return true;
			}else if(annotation instanceof Delete) {
				return true;
			}
		}
		return false;
	}

	private Object invokeGetter(Object entity,String feildName){
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(entity.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				// 过滤class属性
				if (!key.equals("class")&&key.equals(feildName)) {
					// 得到property对应的getter方法
					Method getter = property.getReadMethod();
					Object value = getter.invoke(entity);
					return value;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	private void invokeSetter(Object entity,String feildName,Object value){
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(entity.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				// 过滤class属性
				if (!key.equals("class")&&key.equals(feildName)) {
					// 得到property对应的getter方法
					Method setter = property.getWriteMethod();
					setter.invoke(entity,new Object[]{value});
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	private Map<String, String> handleSqlForParam(String sql) {
		Pattern p = Pattern.compile("\\#\\{(.*?)}");
		// 匹配】
		Matcher matcher = p.matcher(sql);
		// 处理匹配到的值
		Map<String, String> map = new HashMap<>();
		while (matcher.find()) {
			String key=matcher.group().substring(2, matcher.group().length() - 1);
			map.put(key, "?");
			sql=sql.replaceAll("\\#\\{"+key+"\\}","?");
		}
		return map;
	}
	private String handleSql(String sql) {
		Pattern p = Pattern.compile("\\#\\{(.*?)}");
		// 匹配】
		Matcher matcher = p.matcher(sql);
		while (matcher.find()) {
			String key=matcher.group().substring(2, matcher.group().length() - 1);
			sql=sql.replaceAll("\\#\\{"+key+"\\}","?");
		}
		return sql;
	}
	
	 public  Connection getSqlConnection() {
	        //1.JDBC所需的四个参数4个准备：user,password,url,driverClass(连接数据库所需的驱动)
	        String url = "jdbc:mysql://localhost:3306/odobenus?useSSL=false";
	        String user = "root";
	        String password = "123456";
	        String driverClass = "com.mysql.jdbc.Driver";//这个是mysql-connector-java-5.1.8-bin.jar包中的，不是JDK自带的Driver。
	 
	        Connection connection = null;
	        try {
	            //2.加载JDBC驱动程序
	            Class.forName(driverClass);
	            //3.创建数据库连接:三种连接方案
	           connection = DriverManager.getConnection(url,user,password);
	            return connection;
	        } catch (ClassNotFoundException e) {
	            //log.error("Not found driver class, load driver failed.");
	        } catch (SQLException e) {
	            //log.error("create db connection error,",e);
	        }
	        return null;
	    }
}
